package br.ufrgs.inf.cwsmart.api;


import br.ufrgs.inf.cwsmart.data.DataMartManager;
import br.ufrgs.inf.cwsmart.data.DataMartManagerImpl;
import br.ufrgs.inf.cwsmart.data.RepositoryManager;
import br.ufrgs.inf.cwsmart.data.RepositoryManagerImpl;
import br.ufrgs.inf.cwsmart.description.Description;
import br.ufrgs.inf.cwsmart.exception.ExtensionInitFailedException;
import br.ufrgs.inf.cwsmart.exception.FrameworkException;
import br.ufrgs.inf.cwsmart.management.Management;
import br.ufrgs.inf.cwsmart.matcher.MartProperties;
import br.ufrgs.inf.cwsmart.matcher.Matcher;
import br.ufrgs.inf.cwsmart.mining.Mining;
import br.ufrgs.inf.cwsmart.monitoring.Monitoring;
import br.ufrgs.inf.cwsmart.recommendation.Recommendation;


/**
 * A simple abstract factory-like extension point that supplies the actual implementation 
 * of classes being initialized with the class name of the concrete factory.
 *  
 * @author Rodrigo S Lumertz
 */
public abstract class ExtensionFactory
{
	
	/**
	 * The singleton instance of this class' implementation.
	 */
	private static ExtensionFactory singletonInstance;
	
	
	/**
	 * Gets the actual implementation of this <code>ExtensionFactory</code>.
	 * This method can only be called after <code>init</code> has been called.
	 * 
	 * @return The implementation of the <code>ExtansionFactory</code>.
	 * @throws IllegalStateException If <code>init</code> has not been called on
	 *                               the <code>ExtansionFactory</code>.
	 */
	public static ExtensionFactory getExtensionFactory() throws IllegalStateException
	{
		if (singletonInstance == null) {
			throw new IllegalStateException("Registry has not been initialized!");
		} else {
			return singletonInstance;
		}
	}
	
	/**
	 * Initializes this <code>ExtensionFactory</code> with the class name of the
	 * actual implementation.
	 * 
	 * @param implementationClass The name of the class that holds the actual
	 *         implementation.
	 * @throws ExtensionInitFailedException If the class could for some reason not be
	 *          initialized. The Exception contains further information.
	 */
	public static void init(String implementationClass) throws ExtensionInitFailedException
	{
		try
		{
			Class<?> gc = Class.forName(implementationClass);
			Class<? extends ExtensionFactory> ec = gc.asSubclass(ExtensionFactory.class);
			
			singletonInstance = ec.newInstance();
		}
		catch (ClassNotFoundException cnfex) {
			throw new ExtensionInitFailedException("The class '" + implementationClass +
					"' was not found. Check that it is in the class path.", cnfex);
		}
		catch (ExceptionInInitializerError eiiex) {
			throw new ExtensionInitFailedException("The class '" + implementationClass +
					"' could not be loaded, because errors occurred in the initializers.",
					eiiex);
		}
		catch (ClassCastException ccex) {
			throw new ExtensionInitFailedException("The class '" + implementationClass +
					"' is not a subclass of '" + ExtensionFactory.class.getName() +
					"'.", ccex);
		}
		catch (Throwable t) {
			throw new ExtensionInitFailedException("The class '" + implementationClass +
					"' could not be loaded due to an unknown error.", t);
		}
	}
	
	
	///////////////////////////////////////
	//Override this methods if you want
	///////////////////////////////////////
	
	/**
	 * Instantiates a repository manager. This manager
	 * is responsible for all kind of repository acess.
	 * 
	 * @return A repository manager.
	 */
	public RepositoryManager createRepositoryManager() {
		return new RepositoryManagerImpl();
	}
	
	/**
	 * Instantiates a datamart manager. This manager
	 * is responsible for all kind of datamart acess.
	 * 
	 * @return A datamart manager.
	 */
	public DataMartManager createDataMartManager() {		
		return new DataMartManagerImpl();
	}
	
	/**
	 * Instantiates a mart properties object. The class
	 * instantiated is responsible for loading the mart
	 * properties e turn them accessible. 
	 * 
	 * @return A mart properties object
	 * @throws FrameworkException
	 */
	public MartProperties createMartProperties() 
		throws FrameworkException {
		
		return new MartProperties();
	}
	
	///////////////////////////////////////
	// This modules have to be implemented 
	///////////////////////////////////////
	
	/**
	 * Gets an implementation for the <code>Matcher</code> module
	 *  
	 * @return An Matcher module implementation.
	 */
	public abstract Matcher createCWSMatcher();
	
	/**
	 * Gets an implementation for the <code>Description</code> module
	 * 
	 * @return An Description module implementation
	 */
	public abstract Description createCWSDescription();
	
	/**
	 * Gets an implementation for the <code>Recommendation</code> module
	 * 
	 * @return An Recommendation module implementation
	 */
	public abstract Recommendation createCWSRecommendation();
	
	/**
	 * Gets an implementation for the <code>Management</code> module
	 * 
	 * @return An Management module implementation
	 */
	public abstract Management createCWSManagement();
	
	/**
	 * Gets an implementation for the <code>Mining</code> module
	 * 
	 * @return An Mining module implementation
	 */
	public abstract Mining createCWSMining();
	
	/**
	 * Gets an implementation for the <code>Monitoring</code> module
	 * 
	 * @return An Monitoring module implementation
	 */
	public abstract Monitoring createCWSMonitoring();
}